
#import "NSObject+LanBAssociate.h"
#import <objc/runtime.h>

@interface LanBWeaker : NSObject
@property (nonatomic, weak) id obj;
@end
@implementation LanBWeaker
@end

@interface NSObject ()

@property (nonatomic , strong , nullable) NSMutableDictionary *dicAssociateKey;

@end

@implementation NSObject (LanBAssociate)

- (void)lanB_associateWithKey:(const void *)key obj:(id)obj policy:(LanB_Associate)policy
{
    objc_AssociationPolicy associatePolicy = [self.class translateToObjc_Association:policy];
    if (LanB_Associate_Weak == policy) { // 处理 weak 关联
        LanBWeaker *weaker = nil;
        if (obj) {
            weaker = [LanBWeaker new];
            weaker.obj = obj;
        }
        objc_setAssociatedObject(self, key, weaker, associatePolicy);
    }else{
        objc_setAssociatedObject(self, key, obj, associatePolicy);
    }
}

- (id)lanB_associateForKey:(const void *)key
{
    NSObject *result = objc_getAssociatedObject(self, key);
    if ([result isKindOfClass:LanBWeaker.class]) { // 处理 weak 关联
        LanBWeaker *weaker = (LanBWeaker*)result;
        result = weaker.obj;
    }
    return result;
}

-(void (^)(NSString * _Nonnull, id _Nonnull, LanB_Associate))lanB_associate_bind
{
    void (^result)(NSString * _Nonnull, id _Nonnull, LanB_Associate) = ^ void (NSString * key, id obj, LanB_Associate policy) {
        self.dicAssociateKey[key] = key;
        const void * k = (__bridge const void *)(key);
        objc_AssociationPolicy associatePolicy = [self.class translateToObjc_Association:policy];
        if (LanB_Associate_Weak == policy) { // 处理 weak 关联
            LanBWeaker *weaker = nil;
            if (obj) {
                weaker = [LanBWeaker new];
                weaker.obj = obj;
            }
            objc_setAssociatedObject(self, k, weaker, associatePolicy);
        }else{
            objc_setAssociatedObject(self, k, obj, associatePolicy);
        }
    };
    return result;
}

- (id  _Nonnull (^)(NSString * _Nonnull))lanB_associate_value
{
    id _Nonnull (^result)(NSString * _Nonnull) = ^ id (NSString * key) {
        id resultObj;
        if ([self.dicAssociateKey.allKeys containsObject:key]) {
            resultObj = objc_getAssociatedObject(self, (__bridge const void *)(self.dicAssociateKey[key]));
            if ([resultObj isKindOfClass:LanBWeaker.class]) { // 处理 weak 关联
                LanBWeaker *weaker = (LanBWeaker*)resultObj;
                resultObj = weaker.obj;
            }
        }
        return resultObj;
    };
    return result;
}

-(void)lanB_associate_removeAll
{
    objc_removeAssociatedObjects(self);
}


#pragma mark - private

+ (objc_AssociationPolicy)translateToObjc_Association:(LanB_Associate)lanB_Associate
{
    objc_AssociationPolicy policy = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
    
    switch (lanB_Associate) {
        case LanB_Associate_Assign:
            policy = OBJC_ASSOCIATION_ASSIGN;
            break;
        case LanB_Associate_RetainNonatomic:
            policy = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
            break;
        case LanB_Associate_CopyNonatomic:
            policy = OBJC_ASSOCIATION_COPY_NONATOMIC;
            break;
        case LanB_Associate_Retain:
            policy = OBJC_ASSOCIATION_RETAIN;
            break;
        case LanB_Associate_Copy:
            policy = OBJC_ASSOCIATION_COPY;
            break;
        case LanB_Associate_Weak:
            policy = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
            break;
            
        default:
            break;
    }
    
    return policy;
}

#pragma mark - setter getter
- (void)setDicAssociateKey:(NSMutableDictionary *)dicAssociateKey
{
    objc_setAssociatedObject(self, (const void *)(@selector(dicAssociateKey)), dicAssociateKey, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSMutableDictionary *)dicAssociateKey
{
    NSMutableDictionary *result = objc_getAssociatedObject(self, (const void *)(_cmd));
    if (!result) {
        result = [NSMutableDictionary new];
        [self setDicAssociateKey:result];
    }
    return result;
}

@end
